/*-------------------------------------------------------------------------
 *
 * jsonbsubs.c
 *	  jsonb 的下标支持函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/jsonbsubs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "executor/execExpr.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/subscripting.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "utils/jsonb.h"
#include "utils/jsonfuncs.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"


/* SubscriptingRefState.workspace 用于 jsonb 下标执行 */
typedef struct JsonbSubWorkspace
{
	bool		expectArray;	/* 预期 jsonb 根是一个数组 */
	Oid		   *indexOid;		/* 被强制的下标表达式的 OID，可能只为整数或文本 */
	Datum	   *index;			/* Datum 格式的下标值 */
} JsonbSubWorkspace;


/* 对 jsonb 的 SubscriptingRef 表达式完成解析分析。 *  
 * 转换下标表达式，将其强制转换为文本，
 * 并确定 SubscriptingRef 节点的结果类型。 */
static void fc_jsonb_subscript_transform(SubscriptingRef *fc_sbsref,
						  List *fc_indirection,
						  ParseState *fc_pstate,
						  bool fc_isSlice,
						  bool fc_isAssignment)
{
	List	   *fc_upperIndexpr = NIL;
	ListCell   *fc_idx;

	/* 转换和转换下标表达式。Jsonb 下标
 * 不支持切片，只支持上限索引。 */
	foreach(fc_idx, fc_indirection)
	{
		A_Indices  *fc_ai = lfirst_node(A_Indices, fc_idx);
		Node	   *fc_subExpr;

		if (fc_isSlice)
		{
			Node	   *fc_expr = fc_ai->uidx ? fc_ai->uidx : fc_ai->lidx;

			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("jsonb subscript does not support slices"),
					 parser_errposition(fc_pstate, exprLocation(fc_expr))));
		}

		if (fc_ai->uidx)
		{
			Oid			fc_subExprType = InvalidOid,
						fc_targetType = UNKNOWNOID;

			fc_subExpr = transformExpr(fc_pstate, fc_ai->uidx, fc_pstate->p_expr_kind);
			fc_subExprType = exprType(fc_subExpr);

			if (fc_subExprType != UNKNOWNOID)
			{
				Oid			fc_targets[2] = {INT4OID, TEXTOID};

				/* Jsonb 可以处理多种下标类型，但当下标可以被强制转换为多种目标类型时，
 * 必须避免这种情况，类似于重载函数。将来可能会与 jsonpath 扩展。 */
				for (int fc_i = 0; fc_i < 2; fc_i++)
				{
					if (can_coerce_type(1, &fc_subExprType, &fc_targets[fc_i], COERCION_IMPLICIT))
					{
						/* 一种类型已经成功，这意味着有
 * 两个强制目标可能，失败。 */
						if (fc_targetType != UNKNOWNOID)
							ereport(ERROR,
									(errcode(ERRCODE_DATATYPE_MISMATCH),
									 errmsg("subscript type %s is not supported", format_type_be(fc_subExprType)),
									 errhint("jsonb subscript must be coercible to only one type, integer or text."),
									 parser_errposition(fc_pstate, exprLocation(fc_subExpr))));

						fc_targetType = fc_targets[fc_i];
					}
				}

				/* 找不到合适的类型，失败。 */
				if (fc_targetType == UNKNOWNOID)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("subscript type %s is not supported", format_type_be(fc_subExprType)),
							 errhint("jsonb subscript must be coercible to either integer or text."),
							 parser_errposition(fc_pstate, exprLocation(fc_subExpr))));
			}
			else
				fc_targetType = TEXTOID;

			/* 我们知道通过 can_coerce_type 强制转换将成功，因此
 * 可以使用 coerce_type。注意隐式强制转换上下文，
 * 这在处理不同类型的下标时是必需的，
 * 类似于重载函数。 */
			fc_subExpr = coerce_type(fc_pstate,
								  fc_subExpr, fc_subExprType,
								  fc_targetType, -1,
								  COERCION_IMPLICIT,
								  COERCE_IMPLICIT_CAST,
								  -1);
			if (fc_subExpr == NULL)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("jsonb subscript must have text type"),
						 parser_errposition(fc_pstate, exprLocation(fc_subExpr))));
		}
		else
		{
			/* 切片省略了上限。此情况不应发生，因为我们
 * 之前在切片中已经出错，但为了以防万一要处理。 */
			Assert(fc_isSlice && fc_ai->is_slice);
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("jsonb subscript does not support slices"),
					 parser_errposition(fc_pstate, exprLocation(fc_ai->uidx))));
		}

		fc_upperIndexpr = lappend(fc_upperIndexpr, fc_subExpr);
	}

	/* 将转换后的列表存储到 SubscriptRef 节点中 */
	fc_sbsref->refupperindexpr = fc_upperIndexpr;
	fc_sbsref->reflowerindexpr = NIL;

	/* 确定下标操作的结果类型；始终为 jsonb */
	fc_sbsref->refrestype = JSONBOID;
	fc_sbsref->reftypmod = -1;
}

/* 在执行过程中，处理 SubscriptingRef 表达式中的下标。 *  
 * 下标表达式已在 SubscriptingRefState 的数组中以 Datum 形式求值。
 * 根据需要检查并转换它们。 *  
 * 如果任何下标为 NULL，我们在赋值情况下抛出错误，或者在获取
 * 情况下将结果设置为 NULL，并返回 false（指示调用者跳过其余的
 * SubscriptingRef 序列）。 */
static bool fc_jsonb_subscript_check_subscripts(ExprState *fc_state,
								 ExprEvalStep *fc_op,
								 ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref_subscript.state;
	JsonbSubWorkspace *fc_workspace = (JsonbSubWorkspace *) fc_sbsrefstate->workspace;

	/* 如果第一个下标是整数，则期望源 jsonb 是
 * 数组。这些信息不直接使用，所有
 * 这些情况在相应的 jsonb 赋值函数中处理。但
 * 如果源 jsonb 是 NULL，则将使用预期类型来构造
 * 一个空的源。 */
	if (fc_sbsrefstate->numupper > 0 && fc_sbsrefstate->upperprovided[0] &&
		!fc_sbsrefstate->upperindexnull[0] && fc_workspace->indexOid[0] == INT4OID)
		fc_workspace->expectArray = true;

	/* 处理上标 */
	for (int fc_i = 0; fc_i < fc_sbsrefstate->numupper; fc_i++)
	{
		if (fc_sbsrefstate->upperprovided[fc_i])
		{
			/* 如果任何索引表达式返回 NULL，则结果为 NULL 或错误 */
			if (fc_sbsrefstate->upperindexnull[fc_i])
			{
				if (fc_sbsrefstate->isassignment)
					ereport(ERROR,
							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
							 errmsg("jsonb subscript in assignment must not be null")));
				*fc_op->resnull = true;
				return false;
			}

			/* 对于 jsonb 获取和赋值函数，我们需要以文本格式提供路径。
 * 如果不是文本格式，则进行转换。 */
			if (fc_workspace->indexOid[fc_i] == INT4OID)
			{
				Datum		fc_datum = fc_sbsrefstate->upperindex[fc_i];
				char	   *fc_cs = DatumGetCString(DirectFunctionCall1(int4out, fc_datum));

				fc_workspace->index[fc_i] = CStringGetTextDatum(fc_cs);
			}
			else
				fc_workspace->index[fc_i] = fc_sbsrefstate->upperindex[fc_i];
		}
	}

	return true;
}

/* 评估 jsonb 元素的 SubscriptingRef 获取。 *  
 * 源容器在步骤结果变量中（已知不为 NULL，因为
 * 我们将 fetch_strict 设置为 true）。 */
static void fc_jsonb_subscript_fetch(ExprState *fc_state,
					  ExprEvalStep *fc_op,
					  ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	JsonbSubWorkspace *fc_workspace = (JsonbSubWorkspace *) fc_sbsrefstate->workspace;
	Jsonb	   *fc_jsonbSource;

	/* 如果源 jsonb（或任何下标）为 null，则不应到达此处 */
	Assert(!(*fc_op->resnull));

	fc_jsonbSource = DatumGetJsonbP(*fc_op->resvalue);
	*fc_op->resvalue = jsonb_get_element(fc_jsonbSource,
									  fc_workspace->index,
									  fc_sbsrefstate->numupper,
									  fc_op->resnull,
									  false);
}

/* 评估 jsonb 元素赋值的 SubscriptingRef。 *  
 * 输入容器（可能为 null）在结果区域中，替换值在
 * SubscriptingRefState 的 replacevalue/replacenull 中。 */
static void fc_jsonb_subscript_assign(ExprState *fc_state,
					   ExprEvalStep *fc_op,
					   ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;
	JsonbSubWorkspace *fc_workspace = (JsonbSubWorkspace *) fc_sbsrefstate->workspace;
	Jsonb	   *fc_jsonbSource;
	JsonbValue	fc_replacevalue;

	if (fc_sbsrefstate->replacenull)
		fc_replacevalue.type = jbvNull;
	else
		JsonbToJsonbValue(DatumGetJsonbP(fc_sbsrefstate->replacevalue),
						  &fc_replacevalue);

	/* 如果输入容器为 null，设置一个空的 jsonb，
 * 并继续进行赋值。 */
	if (*fc_op->resnull)
	{
		JsonbValue	fc_newSource;

		/* 为了避免任何意外的结果，在期望数组的情况下（即第一个下标为整数），
 * 设置一个空的 jsonb 数组，否则设置为 jsonb 对象。 */
		if (fc_workspace->expectArray)
		{
			fc_newSource.type = jbvArray;
			fc_newSource.val.array.nElems = 0;
			fc_newSource.val.array.rawScalar = false;
		}
		else
		{
			fc_newSource.type = jbvObject;
			fc_newSource.val.object.nPairs = 0;
		}

		fc_jsonbSource = JsonbValueToJsonb(&fc_newSource);
		*fc_op->resnull = false;
	}
	else
		fc_jsonbSource = DatumGetJsonbP(*fc_op->resvalue);

	*fc_op->resvalue = jsonb_set_element(fc_jsonbSource,
									  fc_workspace->index,
									  fc_sbsrefstate->numupper,
									  &fc_replacevalue);
	/* 结果永远不会是 NULL，因此不需要更改 *op->resnull */
}

/* 计算 SubscriptingRef 赋值表达式的旧 jsonb 元素值。
 * 仅在 new-value 子表达式
 * 包含 SubscriptingRef 或 FieldStore 时调用。这与
 * 常规获取情况相同，只是我们必须处理一个 null jsonb，
 * 并且值应存储到 SubscriptingRefState 的
 * prevvalue/prevnull 字段中。 */
static void fc_jsonb_subscript_fetch_old(ExprState *fc_state,
						  ExprEvalStep *fc_op,
						  ExprContext *fc_econtext)
{
	SubscriptingRefState *fc_sbsrefstate = fc_op->d.sbsref.state;

	if (*fc_op->resnull)
	{
		/* 整个 jsonb 为 null，因此任何元素也为 null */
		fc_sbsrefstate->prevvalue = (Datum) 0;
		fc_sbsrefstate->prevnull = true;
	}
	else
	{
		Jsonb	   *fc_jsonbSource = DatumGetJsonbP(*fc_op->resvalue);

		fc_sbsrefstate->prevvalue = jsonb_get_element(fc_jsonbSource,
												   fc_sbsrefstate->upperindex,
												   fc_sbsrefstate->numupper,
												   &fc_sbsrefstate->prevnull,
												   false);
	}
}

/* 为 jsonb 下标操作设置执行状态。与
 * 数组订阅相对，jsonb 类型本身没有嵌套限制，因此下标数没有限制。 */
static void fc_jsonb_exec_setup(const SubscriptingRef *fc_sbsref,
				 SubscriptingRefState *fc_sbsrefstate,
				 SubscriptExecSteps *fc_methods)
{
	JsonbSubWorkspace *fc_workspace;
	ListCell   *lc;
	int			fc_nupper = fc_sbsref->refupperindexpr->length;
	char	   *fc_ptr;

	/* 分配特定于类型的工作空间，以容纳每个下标数据 */
	fc_workspace = palloc0(MAXALIGN(sizeof(JsonbSubWorkspace)) +
						fc_nupper * (sizeof(Datum) + sizeof(Oid)));
	fc_workspace->expectArray = false;
	fc_ptr = ((char *) fc_workspace) + MAXALIGN(sizeof(JsonbSubWorkspace));

	/* 这个编码假设 sizeof(Datum) >= sizeof(Oid)，否则我们可能会
 * 使 indexOid 指针对齐错误 */
	fc_workspace->index = (Datum *) fc_ptr;
	fc_ptr += fc_nupper * sizeof(Datum);
	fc_workspace->indexOid = (Oid *) fc_ptr;

	fc_sbsrefstate->workspace = fc_workspace;

	/* 收集在执行时必要的下标数据类型 */
	foreach(lc, fc_sbsref->refupperindexpr)
	{
		Node	   *fc_expr = lfirst(lc);
		int			fc_i = foreach_current_index(lc);

		fc_workspace->indexOid[fc_i] = exprType(fc_expr);
	}

	/*
	 * 返回指向适当步骤执行函数的指针。
	 */
	fc_methods->sbs_check_subscripts = fc_jsonb_subscript_check_subscripts;
	fc_methods->sbs_fetch = fc_jsonb_subscript_fetch;
	fc_methods->sbs_assign = fc_jsonb_subscript_assign;
	fc_methods->sbs_fetch_old = fc_jsonb_subscript_fetch_old;
}

/* jsonb_subscript_handler  
 * jsonb 的下标处理器。 */
Datum jsonb_subscript_handler(PG_FUNCTION_ARGS)
{
	static const SubscriptRoutines fc_sbsroutines = {
		.transform = fc_jsonb_subscript_transform,
		.exec_setup = fc_jsonb_exec_setup,
		.fetch_strict = true,	/* 对于NULL输入，fetch返回NULL */
		.fetch_leakproof = true,	/* 对于错误的下标，fetch返回NULL */
		.store_leakproof = false	/* ...但赋值抛出错误 */
	};

	PG_RETURN_POINTER(&fc_sbsroutines);
}
